//
//  DuoAccessoryManager.m
//  duosample
//
//  Created by Lin Xu for Discovery Bay Games on 4/4/12.
//  Copyright (c) 2012 Discovery Bay Games. All rights reserved.
//

#include "iConfig.hpp"

#if GLF_COMPILE_WITH_DUO_CONTROLLER_SUPPORT

#import "input/duo/DuoAccessoryManager.h"
#import <ExternalAccessory/ExternalAccessory.h>

NSString* DuoAcessoryWasAddedNotification = @"DUO_ACCESSORY_ADDED_NOTIFICATION";
NSString* DuoAcessoryWasRemovedNotification = @"DUO_ACCESSORY_REMOVED_NOTIFICATION";
NSString* DuoAccessoryObjectKey = @"DUO_ACCESSORY_NOTIFICATION_OBJECT_KEY";

const int MAX_RETRY = 20;

//define it again here so that we don't expose it to the outside
@interface DuoAccessoryDevice () 
- (bool) connectToAccessory:(EAAccessory*)object;
@end
    
@interface DuoAccessoryManager () 
/*{
    NSMutableDictionary* _aliveDevices;
    NSMutableDictionary* _registeredTypes
}*/

- (void) accessoryChanged:(NSNotification*)note;
- (void) enumerateAccessories;

- (bool) handleAddAccessory:(EAAccessory*)obj retriesLeft:(int)retriesLeft;
- (void) handleRemoveAccessory:(DuoAccessoryDevice*)accessory;

- (void) fireAdded:(DuoAccessoryDevice*)accessory;
- (void) fireRemoved:(DuoAccessoryDevice*)accessory;

- (DuoAccessoryDevice*) duoForAccessory:(EAAccessory*)accessory;

@end

@implementation DuoAccessoryManager 

- (id) init {
    if(self = [super init]) {
        _aliveDevices = [[NSMutableDictionary alloc] init];
        _registeredTypes = [[NSMutableDictionary alloc] init];
    }
    return self;
}

- (void)dealloc
{
    [super dealloc];
    [_aliveDevices release];
}

- (void) start 
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(accessoryChanged:) 
                                                 name: EAAccessoryDidConnectNotification 
                                               object:nil];
    
    [[ NSNotificationCenter defaultCenter] addObserver:self
                                              selector:@selector(accessoryChanged:) 
                                                  name:EAAccessoryDidDisconnectNotification 
                                                object: nil];
    
    EAAccessoryManager *accessoryManager = [EAAccessoryManager sharedAccessoryManager]; 
    
    [accessoryManager registerForLocalNotifications];
    
    [self enumerateAccessories];
}
    
- (void) enumerateAccessories
{
    NSArray *accessories= [[EAAccessoryManager sharedAccessoryManager] connectedAccessories];
#ifdef DEBUG
    NSLog([NSString stringWithFormat:@"Accessories connected %d", [accessories count]]);
#endif
	NSMutableDictionary* leftForDead = [NSMutableDictionary dictionaryWithDictionary:_aliveDevices];
	for (EAAccessory *obj in accessories)
	{
        if([self handleAddAccessory:obj retriesLeft:MAX_RETRY]) {
            [leftForDead removeObjectForKey:[NSNumber numberWithInt:(int)obj]];
        }
    }
    
    //all that are left are dead
    for(DuoAccessoryDevice* accessory in leftForDead) {
        [self handleRemoveAccessory:accessory];
    }
}

- (void) stop
{	
    [[NSNotificationCenter defaultCenter] removeObserver:self];

    for(DuoAccessoryDevice* accessory in [_aliveDevices objectEnumerator]) {
        [self fireRemoved:accessory];
    }

    [_aliveDevices removeAllObjects];
    
    [[EAAccessoryManager sharedAccessoryManager] unregisterForLocalNotifications];
}

- (void) accessoryChanged:(NSNotification*)note;
{
    EAAccessory* accessory = [note.userInfo objectForKey:EAAccessoryKey];
    NSString* strings = @"";
    for(NSString* s in accessory.protocolStrings) {
        strings = [NSString stringWithFormat:@"%@, %@", strings, s];
    }
    
    if(note.name == EAAccessoryDidConnectNotification) {
#ifdef DEBUG
        NSLog(@"EAAccessoryDidConnectNotification: %d (connection ID) [PROTOCOLS %@] %d (hash)",
              accessory.connectionID, strings, (int)accessory);
#endif
        [self handleAddAccessory:accessory retriesLeft:MAX_RETRY];
    }
    else if(note.name == EAAccessoryDidDisconnectNotification) {
#ifdef DEBUG
        NSLog(@"EAAccessoryDidDisconnectNotification: %d (connection ID) [PROTOCOLS %@] %d (hash)",
              accessory.connectionID, strings, (int)accessory);
#endif
        DuoAccessoryDevice* duo = [self duoForAccessory:accessory];
        if(duo) {
            [self handleRemoveAccessory:duo];
        }
    }
}

- (void) addAccessoryAddedObserver:(id)target selector:(SEL)selector
{
    [[NSNotificationCenter defaultCenter] addObserver:target 
                                             selector:selector
                                                 name:DuoAcessoryWasAddedNotification
                                               object:nil];
}

- (void) addAccessoryRemovedObserver:(id)target selector:(SEL)selector
{
    [[NSNotificationCenter defaultCenter] addObserver:target 
                                             selector:selector
                                                 name:DuoAcessoryWasRemovedNotification
                                               object:nil];
}


- (void) removeAccessoryObserver:(id)target
{
    [[NSNotificationCenter defaultCenter] removeObserver:target 
                                              forKeyPath:DuoAcessoryWasAddedNotification];
    [[NSNotificationCenter defaultCenter] removeObserver:target 
                                              forKeyPath:DuoAcessoryWasRemovedNotification];
}

- (void) fireAdded:(DuoAccessoryDevice*)accessory
{
    [[NSNotificationCenter defaultCenter] postNotificationName:DuoAcessoryWasAddedNotification
                                                        object:nil
                                                      userInfo:[NSDictionary dictionaryWithObject:accessory 
                                                                                           forKey:DuoAccessoryObjectKey]];
}

- (void) fireRemoved:(DuoAccessoryDevice*)accessory
{
    [[NSNotificationCenter defaultCenter] postNotificationName:DuoAcessoryWasRemovedNotification
                                                        object:nil
                                                      userInfo:[NSDictionary dictionaryWithObject:accessory
                                                                                           forKey:DuoAccessoryObjectKey]];
    
    [accessory stopListening];
}

- (bool) handleAddAccessory:(EAAccessory*)obj retriesLeft:(int)retriesLeft
{
    //see if we already have this accessory 
    if([self duoForAccessory:obj]) 
    {
        return YES;
    }
    
    DuoAccessoryDevice* accessory = nil;
    
    for(NSString* protocol in [obj protocolStrings]) {
        id block = [_registeredTypes objectForKey:protocol];
        if(block != nil) {
            accessory = ((DuoAccessoryDevice*(^)())block)();
        }
        else {
#ifdef DEBUG
            NSLog(@"Ignoring device %d with format %@!", (int)obj, protocol);
#endif
        }
     }
    
    if(accessory) {
        if([accessory connectToAccessory:obj]) {
            [_aliveDevices setObject:accessory forKey:[NSNumber numberWithInt:(int)obj]];
            [self fireAdded:accessory];
        }
        else {
            //reschedule
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 500000000),
                           dispatch_get_main_queue(),
                           ^{
#ifdef DEBUG
                               NSLog(@"Trying again to get device %d", (int)obj);
#endif
                               int k = retriesLeft-1;
                               if(k > 0)
                                   [self handleAddAccessory:obj retriesLeft:k];
                               else {
#ifdef DEBUG
                                   NSLog(@"Failed to get device %d after %d times", (int)obj, MAX_RETRY);
#endif
                               }
                           }
                           );
        }
        return YES;
    }
    
    return NO;
}

- (void) registerProtocol:(NSString*)protocol withBlock:(DuoAccessoryDevice*(^)()) block
{
    [_registeredTypes setObject:Block_copy(block) forKey:protocol];
}

- (void) handleRemoveAccessory:(DuoAccessoryDevice*)accessory
{
    [_aliveDevices removeObjectForKey:[NSNumber numberWithInt:(int)accessory.EAAccessory]];
    [self fireRemoved:accessory];
}

- (DuoAccessoryDevice*) duoForAccessory:(EAAccessory*)accessory
{
    DuoAccessoryDevice* duo = [_aliveDevices objectForKey:[NSNumber numberWithInt:(int)accessory]];
    return duo;
}
    
- (NSEnumerator*) accessories
{
    return [_aliveDevices objectEnumerator];
}

- (void) showAccessoryPicker
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_6_0
    NSString* value = @"Duo Gamer";
    NSPredicate* pred = [NSPredicate predicateWithValue:TRUE];//:value];
    if ([[EAAccessoryManager sharedAccessoryManager]
         respondsToSelector:@selector(showBluetoothAccessoryPickerWithNameFilter:completion:)]){
        [[EAAccessoryManager sharedAccessoryManager] showBluetoothAccessoryPickerWithNameFilter:pred
                                                                                     completion:^(NSError *error) {
                                                                                     }];
    }
#endif
}

+ (DuoAccessoryManager*) sharedManager {
    static DuoAccessoryManager* instance;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[DuoAccessoryManager alloc] init];        
    });
    
    return instance;
}

@end
#endif
